<?php


namespace App\Models;

use App\Constants\AuthType;
use App\Constants\VerifyCodeType;
use App\Exceptions\ApiException;
use App\Services\Base\CacheService;
use App\Services\Base\CodeService;
use App\Services\Base\HelpService;
use App\Traits\BaseTrait;
use Dcat\Admin\Traits\HasDateTimeFormatter;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;

/**
 * This is the model class for table "users"
 *
 * @property string $id
 * @property string $name
 * @property string $nickname
 * @property string $avatar
 * @property string $email
 * @property string $phone
 * @property string $email_verified_at
 * @property string $password
 * @property string $pay_password 支付密码
 * @property string $rid 推荐人
 * @property string $google_captcha 谷歌验证码
 * @property string $google_captcha_new 新的谷歌验证码
 * @property string $google_captcha_check 谷歌验证码确认 0未确认 1确认
 * @property string $status 状态 1启用 0禁用
 * @property string $ip IP
 * @property string $invite_code 邀请码
 * @property string $register_type 1邮箱注册 2手机号注册
 * @property string $area_id 地区
 * @property string $trade_status 交易状态
 * @property string $risk 输赢 1赢 2输 3不干预
 * @property string $created_at
 * @property string $is_sign 是否签约 1是 0否
 * @property string $sign_status 签约状态 0未申请 1审核中 2申请通过
 * @property string $updated_at
 * @property string $agent_path 代理路径
 * @property string $admin_id
 */
class User extends BaseModel
{
    use HasDateTimeFormatter, BaseTrait;

    protected $table = "users";
    protected $guarded = [];

    private $data;

    public const DEFAULT_REGISTER_TYPE = AuthType::CHECK_EMAIL;
    public const DEFAULT_AREA_ID = 0;
    public const DEFAULT_DEVICE_ID = 66;

    public function checkVerifyCode($name, $device_id, $verify_code, $type)
    {
        $codeService = new CodeService();
        $identity = $codeService->getIdentity($name, $device_id, $type);
        $codeService->checkCode($identity, $verify_code, false);
    }

    public function register($data)
    {

        var_dump($data);
        try {
            $commit = false;
            $param = [
                'ip',
//                'invite_code',
                'name',
                'register_type',
                'password',
                'device_id',
                'area_id',
                'verify_code'
            ];
            $this->paramsExist($param, $data);
            AuthType::checkRegister($data['register_type']);
            $this->checkVerifyCode($data['name'], $data['device_id'] ?? User::DEFAULT_DEVICE_ID, $data['verify_code'], VerifyCodeType::TYPE_SIGN_UP);
            $exist = $this->checkUserExist($data['register_type'], $data['name']);

            $userData = [];
            if ($exist['exist']) {
                if ($exist['column'] == 'phone') {
                    $this->error(1004);
                } else {
                    $this->error(1003);
                }
            } else {
                $userData[$exist['column']] = $data['name']; //手机号或者邮箱赋值
            }

            $data['agent_path'] = "1";
            $data['agent_referer'] = "1";
            if ($data['invite_code']) {
                $invite_user = $this->getInviter($data['invite_code']);
                $data['rid'] = $invite_user->id;
                if ($invite_user->admin_id > 0) {
                    $data['agent_path'] = $invite_user->agent_path . "," . $invite_user->admin_id;
                    $data['agent_referer'] = $invite_user->admin_id;
                } else {
                    $data['agent_path'] = $invite_user->agent_path;
                }
            }

            $data['invite_code'] = g_generate_code(6);
            $data['name'] = g_generate_code(16);
            $data['password'] = Hash::make($data['password']);

            $commit = true;
            DB::beginTransaction();
            $userData = array_merge($userData, [
                'invite_code' => $data['invite_code'],
                'name' => $data['name'],
                'password' => $data['password'],
                'ip' => $data['ip'],
                'register_type' => $data['register_type'],
                'area_id' => $data['area_id'],
                'rid' => $data['rid'] ?? 0,
                'agent_path' => $data['agent_path'] ?? "1",
                'agent_referer' => $data['agent_referer'] ?? "1",
            ]);

            /**
             * @var $user User
             */
            $user = $this->getModel()->newQuery()->create($userData);
            if (!$user) {
                $this->error(406);
            }

            UserAsset::query()->create(['uid' => $user->id, 'symbol' => "USD"]);

            if (isset($data['rid'])) {
                UserTree::buildUserTree($user->id, $data['rid']);
            }
            DB::commit();

        } catch (\Exception $e) {
            if ($commit) {
                DB::rollBack();
            }
            HelpService::log($e);
            $this->error($e->getCode(), $e->getMessage());
        }
    }

    public function login($data)
    {
        try {
            $param = ['ip', 'name', 'register_type', 'password', 'device_id'];
            $this->paramsExist($param, $data);

            $user = $this->checkUserExist($data['register_type'], $data['name']);
            if (!$user['exist']) {
                $this->error(1005);
            }
            $user = $user['exist'];
            if (!Hash::check($data['password'], $user->password)) {
                $this->error(1006);
            }

            if ($user->status == STATUS_DISABLED) {
                $this->error(1007);
            }

            $this->setInfo($user);
            return $this->setToken($user, $data['name'], $data['device_id']);
        } catch (\Exception $e) {
            HelpService::log($e);
            $this->error($e->getCode(), $e->getMessage());
        }
    }

    /**
     * @param $data
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null|User
     * @throws ApiException
     */
    public function getUser($data)
    {
        return $this->findUser($data['uid']);
    }

    /**
     * @param $invite_code
     * @return \Illuminate\Database\Eloquent\Builder|Model|object|null|User
     * @throws ApiException
     */
    public function getInviter($invite_code)
    {
        return $this->findUser($invite_code, 'invite_code', 1002);
    }

    /**
     * @param $v
     * @param $k
     * @param $code
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null
     * @throws ApiException
     */
    public function findUser($v, $k = 'id', $code = 1005)
    {
        $user = $this->getModel()->newQuery()->where($k, $v)->first();
        if (!$user) {
            $this->error($code);
        }
        return $user;
    }

    public function checkUserExist($register_type, $name)
    {
        $column = '';
        switch ($register_type) {
            case AuthType::CHECK_EMAIL:
                $column = 'email';
                break;
            case AuthType::CHECK_PHONE:
                $column = 'phone';
                break;
        }
        return [
            'exist' => $this->getModel()->newQuery()->where($column, $name)->first(),
            'column' => $column
        ];
    }

    public function setToken(User $user, $identify, $device_id)
    {
        $token['uid'] = $user->id;
        $token['username'] = $user->name;
        $token['token'] = g_guid_string();
        $token['nickname'] = $user->nickname;
        $token['avatar'] = $user->avatar;
        $token['invite_code'] = $user->invite_code;
        $token['login_time'] = time();
        $token['register_type'] = $user->register_type;
        $token['email_check'] = $user->email ? true : false;
        $token['phone_check'] = $user->phone ? true : false;
        $token['google_captcha_check'] = $user->google_captcha_check ? true : false;
        $token['device_id'] = $device_id;

        $single = false; //true 单登陆 false多登陆

        $channel = Log::channel("token");
        if ($single) {
            //判断是否登陆过，如果登陆则删除之前的登陆信息
            $username_dev_token = $identify;
            $val = CacheService::hGet(R_LOGIN_TOKEN_HASH, $token['uid']);
            $channel->info("用户登陆：(" . $user->id . ")" . $username_dev_token . ",token=" . $token['token']);
            if ($val) {
                $channel->info("用户token删除：(" . $user->id . ")" . $username_dev_token . ",token=" . $val);
                CacheService::hDel(R_LOGIN_TOKEN_INFO, $val);
            }
            //保存登陆信息
            CacheService::hSet(R_LOGIN_TOKEN_HASH, $token['uid'], $token['token']);
            CacheService::hSet(R_LOGIN_TOKEN_INFO, $token['token'], json_encode($token));
        } else {
            //判断是否登陆过，如果登陆则删除之前的登陆信息
            $username_dev_token = $user->id . "-" . $device_id;
            $val = CacheService::hGet(R_LOGIN_TOKEN_HASH_MULTI, $username_dev_token);
            $channel->info("用户登陆：(" . $user->id . ")" . $username_dev_token . ",token=" . $token['token']);
            if ($val) {
                $channel->info("用户token删除：(" . $user->id . ")" . $username_dev_token . ",token=" . $val);
                CacheService::hDel(R_LOGIN_TOKEN_INFO, $val);
            }
            //保存登陆信息
            CacheService::hSet(R_LOGIN_TOKEN_HASH_MULTI, $username_dev_token, $token['token']);
            CacheService::hSet(R_LOGIN_TOKEN_INFO, $token['token'], json_encode($token));
        }
        return $token;
    }


    /**
     * @param $data
     * @return bool
     * @throws ApiException
     */
    public function updatePwd($data): bool
    {
        $user = $this->getUser($data);
        if (!Hash::check($data['old_pwd'], $user->password)) {
            throw new ApiException('旧密码错误');
        }

        $user->password = Hash::make($data['new_pwd']);
        return (bool)$user->save();
    }

    /**
     * @param $data
     * @return bool
     * @throws ApiException
     */
    public function updatePayPwd($data): bool
    {
        $user = $this->getUser($data);
        if (!Hash::check($data['old_pwd'], $user->pay_password)) {
            throw new ApiException('旧密码错误');
        }

        $user->pay_password = Hash::make($data['new_pwd']);
        return (bool)$user->save();
    }


    public function resetPwd($data)
    {
        switch ($data['register_type']) {
            case AuthType::CHECK_EMAIL;
                $user = User::where(['email' => $data['name']])->first();
                if (!$user) {
                    throw new ApiException('', 1022);
                }
                break;
            case AuthType::CHECK_PHONE;
                $user = User::where(['phone' => $data['name']])->first();
                if (!$user) {
                    throw new ApiException('', 1023);
                }
                break;
        }

        $this->checkVerifyCode($data['name'], $data['device_id'] ?? User::DEFAULT_DEVICE_ID, $data['verify_code'], VerifyCodeType::TYPE_FORGET_PASSWORD);
        $user->password = Hash::make($data['password']);
        return (bool)$user->save();
    }


    public function resetPayPwd($data)
    {
        $this->checkVerifyCode($data['name'], $data['device_id'] ?? User::DEFAULT_DEVICE_ID, $data['verify_code'], VerifyCodeType::TYPE_RESET_PAYMENT);
        $user = $this->getUser($data);
        $user->pay_password = Hash::make($data['password']);
        return (bool)$user->save();
    }

    public function referer()
    {
        return $this->belongsTo(User::class, 'rid');
    }

    public function getUserNameAttribute()
    {
        return $this->email;
    }


    public static function getOneSystemUser()
    {
        return ['uid' => 0];
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasOne|UserAsset
     */
    public function asset()
    {
        return $this->belongsTo(UserAsset::class, 'id', 'uid');
    }

    public function assets()
    {
        return $this->hasOne(UserAsset::class, 'uid', 'id');
    }

    public function userBalanceChange($uid, $symbol, $amount, $type, $relate_sn = "", $balance_type = BALANCE_USABLE, $rid = 0)
    {
        /**
         * @var $asset UserAsset
         */
        $asset = $this->asset;
        if ($asset) {
            $result = $asset->assetChange($uid, $symbol, $amount, $type, $relate_sn, $balance_type, $rid);
            if (!$result) {
                throw new ApiException('更新余额失败');
            }
            return $result;
        } else {
            throw new ApiException('资产不存在');
        }

    }

    /**
     * @param $uid
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|User
     * @throws ApiException
     */
    public static function getUserOuter($uid)
    {
        $user = self::query()->find($uid);
        if (!$user) {
            throw new ApiException('用户不存在');
        }
        return $user;
    }

    public function userCenterProfile($uid)
    {
        $user = $this->getUser(['uid' => $uid]);
        $deposit_total = UserDeposit::amountTotal($uid);
        $user_asset = UserAsset::getContractAccount($uid);

        return [
            "userid" => $uid,
            "username" => $user->email,
            "paypasswd_set" => !empty($user->pay_password),
            "total_deposit" => asMoney($deposit_total, 6),
            "asset" => [
                'balance' => asMoney($user_asset->balance),
                'freeze' => asMoney($user_asset->balance_unusable), // freeze+used
            ],
            "verified" => UserAuth::isVerify(['uid' => $uid]),
            "invite_code" => $user->invite_code,
            "invite_url" => self::inviteUrl($user->invite_code),
            "team_enabled" => boolval($user->is_sign),
        ];
    }

    public function isSigned($params)
    {
        $user = self::getUser($params);
        return $user->is_sign == 1;
    }

    public function setSign($params, $status)
    {
        $user_asset = UserAsset::getContractAccount($params['uid']);
        if ($user_asset->balance < (config('base.sign_balance') ?: 50000)) {
            throw new ApiException("余额不足无法签约");
        }

        $user = self::getUser($params);
        if ($status == USER_SIGN_STATUS_SUCCESS) {
            return $user->update([
                'sign_status' => $status,
                'is_sign' => 1
            ]);
        } else {
            return $user->update([
                'sign_status' => $status,
            ]);
        }

    }

    /**
     * @param string $invite_code
     * @return string
     */
    public static function inviteUrl($invite_code)
    {
        $template = config("base.invite_url_template", "https://www.baidu.com/?invite_code={invite_code}");
        return str_replace('{invite_code}', $invite_code, $template);
    }

    /**
     * @param $inviteCode
     * @return User|\Illuminate\Database\Eloquent\Builder|Model|object|null
     */
    public static function oneByInviteCode($inviteCode)
    {
        return self::whereInviteCode(strtoupper(trim($inviteCode)))->first();
    }
}
